# -*- coding: utf-8 -*-

from functools import partial
from decimal import Decimal

from common.utils import math_helper
from .model import BET_TYPE
# from common.lottery.cyclical.ssc.model import SSC_LOTTERY_TYPES
import itertools

RATE_CONFIG = {  # base on UNIT==2
    BET_TYPE.CHOSEN_ONE: 20.000,
    BET_TYPE.CHOSEN_RED_ONE: 10.000,

    BET_TYPE.TWO_DIRECT: 54.286,
    BET_TYPE.TWO_GROUP: 27.143,

    BET_TYPE.THREE_FRONT_DIRECT: 6840.000,
    BET_TYPE.THREE_FRONT_GROUP: 1140.000,

    BET_TYPE.LUCKY_TWO: 6.786,
    BET_TYPE.LUCKY_THREE: 20.357,
    BET_TYPE.LUCKY_FOUR: 69.214,
    BET_TYPE.LUCKY_FIVE: 276.857
}

UNIT = 2  # 彩票单价


def valid_number(bet_type, number):
    '''同一位之间用','隔开
       不同位之间用'|'隔开
       与顺序无关的选项，使用','隔开
    >>> valid_number(BET_TYPE.THREE_FRONT_DIRECT, "1,4,6|2|10,9,8")
    >>> valid_number(BET_TYPE.THREE_FRONT_DIRECT, "1,4,6|2|10,9,1")
    >>> valid_number(BET_TYPE.CHOSEN_ONE, "1,4,6")
    '''
    positions = number.split('|')
    nums = []
    for position in positions:
        if not position:
            continue
        ds = position.split(',')
        for d in ds:
            if bet_type == BET_TYPE.CHOSEN_ONE:
                assert 0 < int(d) <= 18, 'two sum name range error'
            elif bet_type == BET_TYPE.CHOSEN_RED_ONE:
                # big: 大, small: 小, odd: 单, even: 双
                assert 19 <= int(d) <= 20, 'number format wrong'
            else:
                assert 0 < int(d) <= 20, 'number range error'
        nums.append(ds)

    if bet_type % 10 == 7:
        assert len(positions) == 1 and len(positions[0]) >= bet_type / 100, 'Lucky type, format wrong'
    elif bet_type % 10 == 2:
        assert len(positions) == 1 and len(positions[0]) >= bet_type / 100, 'Lucky type, format wrong'
    else:
        assert len(positions) == bet_type / 100 and len(nums) >= 1, 'format wrong'


def calc_total_price(bet_type, number, times=1, unit=UNIT):
    '''根据不同的竞猜方式计算所需总价
    >>> calc_total_price(BET_TYPE.CHOSEN_RED_ONE, "19, 20")
    2
    >>> calc_total_price(BET_TYPE.CHOSEN_ONE, "1,5,6,8,10")
    10
    >>> calc_total_price(BET_TYPE.TWO_DIRECT, "1,5,6|8,10")
    12
    >>> calc_total_price(BET_TYPE.THREE_FRONT_DIRECT, "1,5,6|8,10|2,11")
    24
    >>> calc_total_price(BET_TYPE.LUCKY_THREE, "1,5,6,7")
    8
    '''
    positions = number.split('|')
    nums = []
    for position in positions:
        if not position:
            continue
        ds = position.split(',')
        nums.append(set(ds))

    price = 1

    bet_key = bet_type % 10
    bet_pick = bet_type / 100
    if bet_key == 0:  # 首位直選
        price *= len(nums[0])
    elif bet_key in (2, 7):  # 組選 or 幸運
        price = math_helper.c(len(nums[0]), bet_pick)
    elif bet_type == BET_TYPE.THREE_FRONT_DIRECT or bet_type == BET_TYPE.TWO_DIRECT:
        base = sum(len(each) == len(set(each)) for each in itertools.product(*nums))
        return base * times * unit
    elif bet_key == 1:  # 連直選
        for num in nums:
            price *= len(num)
    else:
        assert False, 'bet_type %s invalid' % bet_type

    return price * times * unit


def generate_win_func(number):
    '''根据开奖结果生成根据押注方式对应的中奖函数，避免重复计算
    '''
    result = {}
    number_int = map(int, number.split(","))

    def first_direct(x):  # 使用者下注
        bet_key = map(int, x.split(','))
        if number_int[0] in bet_key:
            return 1
        else:
            return 0

    result[BET_TYPE.CHOSEN_ONE] = first_direct

    def first_red_direct(x):  # 使用者下注
        bet_key = map(int, x.split(','))
        amount = 0
        for i in bet_key:
            if number_int[0] in [19, 20]:
                amount += 1
        return amount

    result[BET_TYPE.CHOSEN_RED_ONE] = first_red_direct

    def n_front_direct(n, x):
        targets = number_int[:n]
        if len(set(targets)) < n:
            return 0
        guesses = x.split('|')
        all_in = True
        for i, target in enumerate(targets):
            if target not in map(int, guesses[i].split(',')):
                all_in = False
                break
        return 1 if all_in else 0

    result[BET_TYPE.THREE_FRONT_DIRECT] = partial(n_front_direct, 3)

    # def n_direct(n, x): # 使用者下注跟注數
    #     positions = x.split('|')
    #     is_in = True
    #     for i, v in enumerate(number[:n]):
    #         if v not in positions[i]:
    #             is_in = False
    #             break
    #     return 1 if is_in else 0

    def n_all_permutate(n, x):
        positions = x.split('|')
        # bet_number = number_int[:-n+1]
        amount = 0
        for i, v in enumerate(number_int[:-n + 1]):
            for index in range(n):
                if number_int[i + index] not in map(int, positions[index].split(',')):
                    break
            else:
                amount += 1
        return amount

    result[BET_TYPE.TWO_DIRECT] = partial(n_all_permutate, 2)

    def n_all_permutate_combinate(n, x):
        bet_key = map(int, x.split(','))
        bet_number = number_int[:-n + 1]
        amount = 0
        for i, v in enumerate(number_int[:-n + 1]):
            for index in range(n):
                if number_int[i + index] not in bet_key:
                    break
            else:
                amount += 1
        return amount

    result[BET_TYPE.TWO_GROUP] = partial(n_all_permutate_combinate, 2)

    def n_front_combinate(n, x):
        nums = map(int, x.split(','))
        is_in = True
        for i, v in enumerate(number_int[:n]):
            if v not in nums:
                is_in = False
                break
        return 1 if is_in else 0

    result[BET_TYPE.THREE_FRONT_GROUP] = partial(n_front_combinate, 3)

    def n_lucky(n, x):
        nums = map(int, x.split(','))
        lucky = 0
        for v in number_int:
            if v in nums:
                lucky += 1
        if lucky >= n:
            return math_helper.c(lucky, n)
        return 0

    result[BET_TYPE.LUCKY_TWO] = partial(n_lucky, 2)
    result[BET_TYPE.LUCKY_THREE] = partial(n_lucky, 3)
    result[BET_TYPE.LUCKY_FOUR] = partial(n_lucky, 4)
    result[BET_TYPE.LUCKY_FIVE] = partial(n_lucky, 5)

    return result


def calc_win_price(funcs, bet_number, bet_type, times=1, unit=UNIT,
                   rate_config=RATE_CONFIG):
    '''根据开奖结果和押注方式/倍数，计算中奖金额
    '''
    func = funcs.get(bet_type)
    base = func(bet_number) * Decimal(RATE_CONFIG.get(bet_type))
    return Decimal(base) * Decimal(times) * Decimal(unit) / Decimal(UNIT)

